package com.small.sentinel.slots.block.degrade.circuitbreaker;

import com.small.sentinel.Context;
import com.small.sentinel.slots.block.degrade.DegradeRule;
import com.small.sentinel.slots.block.degrade.DegradeRuleManager;
import com.small.sentinel.util.TimeUtil;

import java.util.concurrent.atomic.AtomicReference;

public abstract class AbstractCircuitBreaker implements CircuitBreaker {
    //限流规则
    protected final DegradeRule rule;

    // 恢复超时时间（ms）--- rule.timeWindow --- 熔断时间
    protected final int recoveryTimeoutMs;

    // 断路器状态
    protected final AtomicReference<State> currentState = new AtomicReference<>(State.CLOSED);

    // 半开状态下，下次重试时间戳
    protected volatile long nextRetryTimestamp;

    AbstractCircuitBreaker(DegradeRule rule) {
        //校验规则
        if (!DegradeRuleManager.isValidRule(rule)) {
            throw new IllegalArgumentException("Invalid DegradeRule: " + rule);
        }
        this.rule = rule;
        this.recoveryTimeoutMs = rule.getTimeWindow() * 1000;
    }

    @Override
    public DegradeRule getRule() {
        return rule;
    }

    @Override
    public boolean tryPass(Context context) {
        State state = currentState.get();
        if (state == State.CLOSED) {
            return true;
        }
        if (state == State.OPEN) {
            //达到重试超时时间并且从尝试从打开到半开状态
            if (retryTimeoutArrived() && fromOpenToHalfOpen()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public State currentState() {
        return currentState.get();
    }


    /**
     * 从关闭到打开状态
     *
     * @return
     */
    protected boolean fromCloseToOpen() {

        if (currentState.compareAndSet(currentState.get(), State.OPEN)) {
            updateNextRetryTimestamp();
            return true;
        }
        return false;
    }

    /**
     * 从打开到半开状态
     */
    protected boolean fromOpenToHalfOpen() {

        if (currentState.compareAndSet(State.OPEN, State.HALF_OPEN)) {
            return true;
        }
        return false;
    }


    /**
     * 从半开到关闭
     * @return
     */
    protected boolean fromHalfOpenToClose() {
        if (currentState.compareAndSet(State.HALF_OPEN, State.CLOSED)) {
            //重置统计信息
            resetStat();
            return true;
        }
        return false;
    }

    /**
     * 重置统计信息
     */
    abstract void resetStat();

    /**
     * 从半开到打开
     * @return
     */
    protected boolean fromHalfOpenToOpen() {
        if (currentState.compareAndSet(State.HALF_OPEN, State.OPEN)) {
            //更新下次重试时间
            updateNextRetryTimestamp();
            return true;
        }
        return false;
    }

    /**
     * 转换为打开
     */
    protected void transformToOpen() {
        State cs = currentState.get();
        switch (cs) {
            case CLOSED:
                fromCloseToOpen();
                break;
            case HALF_OPEN:
                fromHalfOpenToOpen();
                break;
            default:
                break;
        }
    }

    /**
     * 更新下次重试时间
     */
    protected void updateNextRetryTimestamp() {
        this.nextRetryTimestamp = TimeUtil.currentTimeMillis() + recoveryTimeoutMs;
    }

    /**
     * 到达重试超时时间
     *
     * @return
     */
    protected boolean retryTimeoutArrived() {
        return TimeUtil.currentTimeMillis() >= nextRetryTimestamp;
    }
}
